{
 "cells": [
  {
   "metadata": {
    "collapsed": true
   },
   "cell_type": "markdown",
   "source": [
    "#### 两种自定义方式\n",
    "第一种：使用@tool装饰器(自定义工具的最简单方式)\n",
    "    装饰器默认使用函数名称作为工具名称，但可以通过参数name_or_callable来覆盖此设置\n",
    "    同时，装饰器将使用函数的文档字符串作为工具的描述，因此函数必须提供文档字符串\n",
    "\n",
    "第二种：使用StructureTool.from_function类方法\n",
    "    这类似于@tool装饰器，但允许更多配置和同步/异步实现的规范"
   ],
   "id": "13c5eb04b2059b61"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "#### 几个常用属性\n",
    "Tool由几个常用属性组成\n",
    "    name                str      必选，在提供给LLM或Agent的工具集中必须是唯一的\n",
    "    description         str      可选但建议，描述工具的功能，LLM或Agent将使用此描述作为上下文，使它确定工具的使用\n",
    "    args_schema         PydanticBaseModel       可选但建议，可用于提供更多信息(例如few-shot示例)或验证预期参数"
   ],
   "id": "dd0c8953d3334833"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "##### 使用@tool装饰器定义工具",
   "id": "1d7e539b9de99e00"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "###### 举例1",
   "id": "208ba886b022fa0f"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "from langchain_core.tools import tool\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "class FieldInfo(BaseModel):\n",
    "    a:int = Field(description=\"第一个整形参数\")\n",
    "    b:int = Field(description=\"第二个整形参数\")\n",
    "\n",
    "@tool(name_or_callable=\"add_number\", description=\"add two numbers\", return_direct=True, args_schema=FieldInfo)\n",
    "def add_number(a:int, b:int) -> int:\n",
    "    return a + b\n",
    "\n",
    "print(f\"name = {add_number.name}\")\n",
    "print(f\"args = {add_number.args}\")\n",
    "print(f\"description = {add_number.description}\")\n",
    "print(f\"return_redirect = {add_number.return_direct}\")\n",
    "# 调用工具\n",
    "add_number.invoke({\"a\":10, \"b\":20})"
   ],
   "id": "fe55deafa86dd5c2",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "##### 使用StructuredTool的from_function()\n",
    "StructuredTool.from_function类方法提供了比@tool装饰器更多的可配属性，而无需太多额外的代码"
   ],
   "id": "fa086dcd89ab395"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "###### 举例1",
   "id": "45ea891aafbfec64"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "from langchain_core.tools import StructuredTool\n",
    "# 声明一个函数\n",
    "def search_google(query: str):\n",
    "    return \"最后查询的结果是\"\n",
    "\n",
    "# 定义一个工具\n",
    "search01 = StructuredTool.from_function(func=search_google, name=\"Search\",\n",
    "                                        description=\"查询google搜索引擎，并将结果返回\")\n",
    "\n",
    "print(f\"name = {search01.name}\")\n",
    "print(f\"args = {search01.args}\")\n",
    "print(f\"description = {search01.description}\")\n",
    "search01.invoke({\"query\":\"中英AI的发展现状\"})"
   ],
   "id": "c204d65339517e98",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "##### 工具调用举例\n",
    "通过大模型分析用户需求，判断是否需要调用指定工具"
   ],
   "id": "cb6cfb4ac568e344"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.messages import HumanMessage\n",
    "from langchain_community.tools import MoveFileTool\n",
    "from langchain_core.utils.function_calling import convert_to_openai_function\n",
    "\n",
    "os.environ['OPENAI_BASE_URL'] = 'https://vip.apiyi.com/v1'\n",
    "os.environ['OPENAI_API_KEY'] = 'sk-xU64G4hXJ4L47ko3764958119dB245D2BdEcE528767dA1Da'\n",
    "\n",
    "# 实例化大模型\n",
    "chat_model = ChatOpenAI(model=\"gpt-4o-mini\")\n",
    "# 获取工具列表\n",
    "tools = [MoveFileTool()]\n",
    "functions = [convert_to_openai_function(t) for t in tools]\n",
    "# 获取消息列表\n",
    "messages = [HumanMessage(content=\"将文件a移动到桌面\")]\n",
    "# 调用大模型(传入消息列表、工具的列表)\n",
    "# 因为大模型invoke调用时，需要传入函数的列表，所以需要将工具转换为函数\n",
    "# response = chat_model.invoke(input=messages, functions=functions)\n",
    "# print(response)\n",
    "\n",
    "messages1 = [HumanMessage(content=\"查询一下明天北京的天气\")]\n",
    "\n",
    "response = chat_model.invoke(input=messages1, functions=functions)\n",
    "print(response)"
   ],
   "id": "48a3f5428177a765",
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
